using System;
using System.Web.Mvc;
using System.Web.Routing;


namespace Wayloop.Web.Mvc.Routing
{
    public class Area : IArea, IRoutableArea
    {
        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name { get; protected set; }


        /// <summary>
        /// Gets or sets the namespace.
        /// </summary>
        /// <value>The namespace.</value>
        public string Namespace { get; protected set; }


        /// <summary>
        /// Gets or sets the routes.
        /// </summary>
        /// <value>The routes.</value>
        public RouteCollection Routes { get; protected set; }


        /// <summary>
        /// Initializes a new instance of the <see cref="Area"/> class.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="namespace">The area namespace.</param>
        /// <param name="routes">The routes.</param>
        public Area(string name, string @namespace, RouteCollection routes)
        {
            Name = name;
            Namespace = @namespace;
            Routes = routes;
        }


        /// <summary>
        /// Adds the specified routes to this area.
        /// </summary>
        /// <param name="area">The target area.</param>
        public void WithRoutes(Action<IRoutableArea> area)
        {
            area(this);
        }


        /// <summary>
        /// Adds a route for the specified URL.
        /// </summary>
        /// <param name="url">The URL.</param>
        public void AddRoute(string url)
        {
            AddRoute(null, url, null, null);
        }


        /// <summary>
        /// Adds a route for the specified URL with the specified default URL parameter values.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="defaults">The default URL parameter values.</param>
        public void AddRoute(string url, object defaults)
        {
            AddRoute(null, url, defaults, null);
        }


        /// <summary>
        /// Adds a route for the specified URL with the specified default URL parameter values.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="defaults">The default URL parameter values.</param>
        public void AddRoute(string url, RouteValueDictionary defaults)
        {
            AddRoute(null, url, defaults, null);
        }


        /// <summary>
        /// Adds a route for the specified URL with the specified default URL parameter values and constraints.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="defaults">The default URL parameter values.</param>
        /// <param name="constraints">The URL parameter value constraints.</param>
        public void AddRoute(string url, object defaults, object constraints)
        {
            AddRoute(null, url, defaults, constraints);
        }


        /// <summary>
        /// Adds a route for the specified URL with the specified default URL parameter values and constraints.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="defaults">The default URL parameter values.</param>
        /// <param name="constraints">The URL parameter value constraints.</param>
        public void AddRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints)
        {
            AddRoute(null, url, defaults, constraints);
        }


        /// <summary>
        /// Adds a named route for the specified URL.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="url">The URL.</param>
        public void AddRoute(string name, string url)
        {
            AddRoute(name, url, null, null);
        }


        /// <summary>
        /// Adds a named route for the specified URL with the specified default URL parameter values.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="url">The URL.</param>
        /// <param name="defaults">The default URL parameter values.</param>
        public void AddRoute(string name, string url, object defaults)
        {
            AddRoute(name, url, defaults, null);
        }


        /// <summary>
        /// Adds a named route for the specified URL with the specified default URL parameter values.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="url">The URL.</param>
        /// <param name="defaults">The default URL parameter values.</param>
        public void AddRoute(string name, string url, RouteValueDictionary defaults)
        {
            AddRoute(name, url, defaults, null);
        }


        /// <summary>
        /// Adds a route for the specified URL with the specified default URL parameter values and constraints.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="url">The URL.</param>
        /// <param name="defaults">The default URL parameter values.</param>
        /// <param name="constraints">The URL parameter value constraints.</param>
        public void AddRoute(string name, string url, object defaults, object constraints)
        {
            AddRoute(name, url, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints));
        }


        /// <summary>
        /// Adds a route for the specified URL with the specified default URL parameter values and constraints.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="url">The URL.</param>
        /// <param name="defaults">The default URL parameter values.</param>
        /// <param name="constraints">The URL parameter value constraints.</param>
        public void AddRoute(string name, string url, RouteValueDictionary defaults, RouteValueDictionary constraints)
        {
            var route = new LowercaseRoute(url, new MvcRouteHandler()) {
                Defaults = new RouteValueDictionary(new { area = Name }),
                Constraints = new RouteValueDictionary(new { area = Name }),
                DataTokens = new RouteValueDictionary(new { namespaces = new[] { Namespace } }),
            };

            if (defaults != null) {
                foreach (var pair in defaults) {
                    if (route.Defaults.ContainsKey(pair.Key)) {
                        continue;
                    }

                    route.Defaults.Add(pair.Key, pair.Value);
                }
            }

            if (constraints != null) {
                foreach (var pair in constraints) {
                    if (route.Constraints.ContainsKey(pair.Key)) {
                        continue;
                    }

                    route.Constraints.Add(pair.Key, pair.Value);
                }
            }

            if (String.IsNullOrEmpty(name)) {
                Routes.Add(route);
            }
            else {
                Routes.Add(name, route);
            }
        }
    }
}